#include <stdlib.h>
#include <typeinfo>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "TableSymboles.h"
#include "Valeur.h"
#include "ErreurEx.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudSeqInst::getValeur() {
    Valeur* val = new ValeurEntiere(0);
    for (unsigned int i = 0; i < tabInst.size(); i++)
        val = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return val; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}
////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficherAda() {
    for (unsigned int i = 0; i < tabInst.size(); i++) {
        tabInst[i]->afficherAda(); // on affiche les fils en augmentant l'indentation
        cout << ";" << endl;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudAffectation::getValeur() {
     Valeur* val=NULL;
     Valeur* valExp = expression->getValeur();
    if (typeid (*valExp) == typeid (ValeurEntiere)) {       
        val = new ValeurEntiere(((ValeurEntiere*)valExp)->getValeur());
        ((SymboleValue*)variable)->setValeur(val);
        return val;
    } else if (typeid (*valExp) == typeid (ValeurChaine)) {
        val = new ValeurChaine(((ValeurChaine*)valExp)->getValeur());
        ((SymboleValue*)variable)->setValeur(val);
        return val;        
    } else if (typeid (*valExp) == typeid (ValeurFlottant)) {
         val = new ValeurFlottant(((ValeurFlottant*)valExp)->getValeur());
        ((SymboleValue*)variable)->setValeur(val);
        return val;
    }
    else
        return NULL;
    // par convention, une affectation a pour valeur la valeur affectée

}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}
////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficherAda() {
    variable->afficherAda();
    cout << "=";
    expression->afficherAda();
}
////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
template <typename T>
T Calcul(T og, T od, Symbole operateur) {
    if (operateur == "+") return (og + od);
    else if (operateur == "-") return (og - od);
    else if (operateur == "et") return (og and od);
    else if (operateur == "ou") return (og or od);
    //else if (operateur == "non") return (-od);
    else if (operateur == "==") return (og == od);
    else if (operateur == "*") return (og * od);
    else if (operateur == "<") return (og < od);
    else if (operateur == "<=") return (og <= od);
    else if (operateur == ">") return (og > od);
    else if (operateur == ">=") return (og >= od);
    else // operateur=="/"
        if (od != 0)
        return (og / od);
    else {
        cout << "Erreur pendant l'interpretation : division par zero" << endl;        
        throw ErreurEx();
    }    
}

Valeur* NoeudOperateurBinaire::getValeur() {
    Valeur* val=NULL;  
    Valeur* opG = operandeGauche->getValeur();
    Valeur* opD = operandeDroit->getValeur();    
    if (typeid (*opG) == typeid (ValeurEntiere)) {
        int og = ((ValeurEntiere*)opG)->getValeur();
        if (typeid (*opD) == typeid (ValeurEntiere)) {
            int od = ((ValeurEntiere*)opD)->getValeur();
            val = new ValeurEntiere(Calcul<int>(og, od, this->operateur));
        } else
//            throw ErreurEx();
            cout << "Erreur de type entier";
    }
    else if (typeid (*opG) == typeid (ValeurFlottant)){
        float og = ((ValeurFlottant*)opG)->getValeur();
        if(typeid (*opD) == typeid (ValeurFlottant)){
            float od = ((ValeurFlottant*)opD)->getValeur();
            val = new ValeurFlottant(Calcul<float>(og,od,this->operateur));
        }
        else            
            throw ErreurEx();        
    }   
    else if (typeid (*opG) == typeid (ValeurChaine)) {
        string og = ((ValeurChaine*)opG)->getValeur();
        if (typeid (*opD) == typeid (ValeurChaine)) {
            string od = ((ValeurChaine*)opD)->getValeur();           
            if (this->operateur == "+") val = new ValeurChaine(og + od);
            else if (this->operateur == "==") val = new ValeurEntiere(og == od);
            else if (this->operateur == "<") val = new ValeurEntiere(og < od);
            else if (this->operateur == "<=") val = new ValeurEntiere(og <= od);
            else if (this->operateur == ">") val = new ValeurEntiere(og > od);
            else if (this->operateur == ">=") val = new ValeurEntiere(og >= od);
        } else
            cout << "Erreur de type chaine";
            //throw ErreurEx();
    }
    return val;    
}
////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}
////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficherAda() {
    operandeGauche->afficherAda();
    if(operateur.getChaine()=="et")
        cout << " and ";
    else if (operateur.getChaine()=="ou")
            cout << " or ";
    else if(operateur.getChaine()=="non")
        cout << " no" ;
    else
    cout << operateur.getChaine();
    operandeDroit->afficherAda();
}
////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire(Noeud* fact) {
    this->fact = fact;    
}
////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudOperateurUnaire::getValeur() {
    Valeur* val=fact->getValeur();
    if(typeid(*val)==typeid(ValeurEntiere))        
        val = new ValeurEntiere(-((ValeurEntiere*)(SymboleValue*)val)->getValeur());
    else if(typeid(*val)==typeid(ValeurFlottant))
        val = new ValeurFlottant(-((ValeurFlottant*)(SymboleValue*)val)->getValeur());
    return val;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Unaire \"" << "-" << "\" applique a : " << endl;
    fact->afficher(indentation+1);    
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficherAda() {
    cout << "-";
    fact->afficherAda();
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi() : tabCond(), tabSeqInst() {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstSi::getValeur() {
    int unsigned i = 0;
    Valeur* val = tabCond[i]->getValeur();
    if (typeid (*val) == typeid (ValeurEntiere))
        while(i < tabCond.size() && tabCond[i] != NULL && !(((ValeurEntiere*) (tabCond[i]->getValeur()))->getValeur()))
            i++;    
    else if (typeid (*val) == typeid (ValeurChaine))
        while(i < tabCond.size() && tabCond[i] != NULL)
            i++;   
    else if (typeid (*val) == typeid (ValeurFlottant))
        while (i < tabCond.size() && tabCond[i] != NULL && !(((ValeurFlottant*) (tabCond[i]->getValeur()))->getValeur()))
            i++;
    
    if (i < tabCond.size())
        tabSeqInst[i]->getValeur();
    
    return val=NULL;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Si : " << tabCond.size() << " condition(s)" << endl;
    
    for (unsigned int i = 0; i < tabCond.size(); i++) {
        if (tabCond[i] != NULL)
            tabCond[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
        tabSeqInst[i]->afficher(indentation + 1);
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficherAda() {
    for (unsigned int i = 0; i < tabCond.size(); i++) {
        if (i == 0) {
            cout << "if ";
            tabCond[i]->afficherAda(); // on affiche les fils en augmentant l'indentation
            cout << " then" << endl;
        } else if (i != 0 && tabCond[i] != NULL) {
            cout << "elsif ";
            tabCond[i]->afficherAda(); // on affiche les fils en augmentant l'indentation
            cout << " then" << endl;
        } else
            cout << "else" << endl;
        tabSeqInst[i]->afficherAda();
    }
    cout << "endif";
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::ajoute(Noeud* condition, Noeud* seqInst) {
    tabCond.push_back(condition);
    tabSeqInst.push_back(seqInst);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* cond, Noeud* seqInst) : cond(cond), seqInst(seqInst) {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstTq::getValeur() {
    Valeur* val=cond->getValeur();
    if(typeid(*val)==typeid(ValeurEntiere)){      
   while(((ValeurEntiere*)cond->getValeur())->getValeur())
        seqInst->getValeur();
    }
    else if(typeid(*val)==typeid(ValeurFlottant)){
        while(((ValeurFlottant*)cond->getValeur())->getValeur())
        seqInst->getValeur();
    }
    
    return val;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::afficher(unsigned short indentation) // affiche la séquence d'instructions   
{
    cout << "Noeud - Instruction TantQue : " << endl;
    cond->afficher(indentation + 1);
    seqInst->afficher(indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstTq::afficherAda() {
    cout << "while ";
    cond->afficherAda();
    cout << " loop" << endl;
    seqInst->afficherAda();
    cout << "endloop";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* cond, Noeud* seqInst) : cond(cond), seqInst(seqInst) {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstRepeter::getValeur() {
    Valeur* val=cond->getValeur();
    if(typeid (*val)==typeid(ValeurEntiere))
    do seqInst->getValeur(); while(((ValeurEntiere*)cond->getValeur())->getValeur());
    else if(typeid (*val)==typeid(ValeurFlottant))
    do seqInst->getValeur(); while( ((ValeurFlottant*)cond->getValeur())->getValeur());
       
    return val;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficher(unsigned short indentation) // affiche la séquence d'instructions   
{
    cout << "Noeud - Instruction Repeter : " << endl;
    seqInst->afficher(indentation + 1);
    cond->afficher(indentation + 1);
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficherAda() {
    cout << "loop" << endl;
    seqInst->afficherAda();
    cout << "exit when ";
    cond->afficherAda();
    cout << ";" << endl << "end loop";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* affection1, Noeud* affection2, Noeud* rela, Noeud* seq) {
    this->affection[0] = affection1;
    this->affection[1] = affection2;
    this->cond = rela;
    this->seqInst = seq;
}
////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstPour::getValeur() {    
    Valeur* val=affection[0]->getValeur();   
    if(typeid (*val)==typeid(ValeurEntiere)){        
    for (((ValeurEntiere*)affection[0]->getValeur())->getValeur(); ((ValeurEntiere*)cond->getValeur())->getValeur(); ((ValeurEntiere*)affection[1]->getValeur())->getValeur())
        seqInst->getValeur();
    }
    else if (typeid (*val)==typeid(ValeurFlottant))
    for (((ValeurFlottant*)affection[0]->getValeur())->getValeur(); ((ValeurFlottant*)cond->getValeur())->getValeur(); ((ValeurFlottant*)affection[1]->getValeur())->getValeur())
        seqInst->getValeur();
    
    return val;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::afficher(unsigned short indentation) // affiche la séquence d'instructions  
{
    cout << "Noeud - Instruction Pour : " << endl;
    affection[0]->afficher(indentation + 1);
    cond->afficher(indentation + 1);
    affection[1]->afficher(indentation + 1);

}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::afficherAda() {
    cout << "while ";
    cond->afficherAda();
    cout << " loop" << endl;
    affection[1]->afficherAda();
    cout << ";" << endl << "endloop";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable) : variable(variable) {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstLire::getValeur() {
    cout << "Attente saisie ..." << endl;
        Valeur* valeur= variable->getValeur();
    if(typeid (*valeur) == typeid(ValeurEntiere)){         
        int val;
        cin >> val;        
        valeur = new ValeurEntiere(val);
    }
    else if (typeid(*valeur)==typeid(ValeurFlottant)){         
        float val;
        cin >> val;
        valeur = new ValeurFlottant(val);
    }
    else if (typeid (*valeur) == typeid(ValeurChaine)){        
        string val;
        cin >> val;
        valeur = new ValeurChaine(val);
    } 
    
        
    ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable    
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficher(unsigned short indentation) {
    cout << "Noeud - Instriction Lire : " << endl;
    variable->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficherAda() {
    cout << "lire(";
    variable->afficherAda();
    cout << ")";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* aEcrire) : aEcrire(aEcrire) {
}
////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstEcrire::getValeur() {
    Valeur* val=NULL;
    if (((SymboleValue*) aEcrire)->estDefini()){
        val = aEcrire->getValeur();
    if(typeid(*val)==typeid(ValeurEntiere))
        cout << ((ValeurEntiere*)val)->getValeur() << endl;
    else if (typeid(*val)==typeid(ValeurFlottant))
        cout << ((ValeurFlottant*)val)->getValeur() << endl;
    else if (typeid(*val)==typeid(ValeurChaine))
        cout << ((ValeurChaine*)val)->getValeur() << endl;
    }    
    return val;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::afficher(unsigned short indentation) // affiche la séquence d'instructions  
{
    cout << "Noeud - Instruction Ecrire : " << endl;
    aEcrire->afficher(indentation + 1);

}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::afficherAda() {
    cout << "ecrire(";
    aEcrire->afficherAda();
    cout << ")";
}

